/*
 * Copyright (C) 2011, EADS France
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 */

package xowl.generator.standalone;

/**
 *
 * @author Laurent WOUTERS
 */
public class PropImplWriter {
    
    public static void write(java.io.Writer writer, xowl.generator.PropertyImplementation impl) throws java.io.IOException {
        boolean isInTypeRestrictionChain = false;
        for (xowl.generator.PropertyInterface inter : impl.getInterfaces()) {
            if (inter.isInTypeRestrictionChain()) {
                isInTypeRestrictionChain = true;
                break;
            }
        }
        String property = impl.getProperty().getName();
        property = String.valueOf(property.charAt(0)).toUpperCase() + property.substring(1);
        
        writer.write("    // <editor-fold defaultstate=\"collapsed\" desc=\"Property " + impl.getProperty().getName() + "\">\n");
        if (impl.getDomain() == impl.getParentClass()) {
            if (impl.getProperty().isObjectProperty()) write_PropertyClass_Interface(writer, impl.getRangeClass().getJavaName(), impl);
            else write_PropertyClass_Interface(writer, impl.getRangeDatatype().getJavaType(), impl);
        }
        if (impl.getProperty().isObjectProperty()) {
            if (impl.getRange() == null || impl.getRange() == impl.getRangeClass())
                write_PropertyClass_Implementation_SameType(writer, impl.getRangeClass().getJavaName(), impl);
            else
                write_PropertyClass_Implementation_Transtype(writer, impl.getRange().getJavaName(), impl.getRangeClass().getJavaName(), impl);
        } else {
            write_PropertyClass_Implementation_SameType(writer, impl.getRangeDatatype().getJavaType(), impl);
        }
        writer.append("    private " + impl.getProperty().getName() + "_impl data" + property + ";\n");
        writer.append("    public " + impl.getDomain().getJavaName() + "." + impl.getProperty().getName() + " __getImplOf" + impl.getProperty().getName() + "() { return data" + property + "; }\n");

        if (!impl.getProperty().isObjectProperty())
            write_Datatype(writer, impl);
        else {
            for (xowl.generator.PropertyInterface inter : impl.getInterfaces())
                write_ObjectInterface(writer, inter, impl, isInTypeRestrictionChain);
        }
        writer.write("    // </editor-fold>\n");
    }
    private static void write_PropertyClass_Interface(java.io.Writer writer, String type, xowl.generator.PropertyImplementation impl) throws java.io.IOException {
        writer.append("    public static interface " + impl.getProperty().getName());
        writer.append(" {\n");
        writer.append("        boolean check_contains(" + type + " elem);\n");

        writer.append("        boolean user_check_add(" + type + " elem);\n");
        writer.append("        boolean user_check_remove(" + type + " elem);\n");
        writer.append("        boolean user_check_replace(" + type + " oldElem, " + type + "  newElem);\n");
        writer.append("        void user_add(" + type + " elem);\n");
        writer.append("        void user_remove(" + type + " elem);\n");
        
        writer.append("        boolean inverse_check_add(" + type + " elem);\n");
        writer.append("        boolean inverse_check_remove(" + type + " elem);\n");
        writer.append("        boolean inverse_check_replace(" + type + " oldElem, " + type + "  newElem);\n");
        writer.append("        void inverse_add(" + type + " elem);\n");
        writer.append("        void inverse_remove(" + type + " elem);\n");
        writer.append("    }\n");
    }
    private static void write_PropertyClass_Implementation_Header(java.io.Writer writer, xowl.generator.PropertyImplementation impl) throws java.io.IOException {
        writer.append("    private static class " + impl.getProperty().getName() + "_impl");
        writer.append(" implements " + impl.getDomain().getJavaName() + "." + impl.getProperty().getName() + " {\n");
    }
    private static void write_PropertyClass_Implementation_InnerData(java.io.Writer writer, String type, xowl.generator.PropertyImplementation impl) throws java.io.IOException {
        writer.append("        private " + impl.getParentClass().getJavaName() + " domain;\n");
        if (!impl.isVector()) {
            writer.append("        private " + type + " data;\n");
            writer.append("        public " + type + " get_raw() { return data; }\n");
            if (!impl.hasValues())
                writer.append("        public " + type + " get() { return data; }\n");
            else {
                if (impl.hasSelf())
                    writer.append("        public " + type + " get() { return domain; }\n");
                else if (impl.getProperty().isObjectProperty()) {
                    xowl.generator.InstanceGenerator instance = impl.hasObjectValues().get(0);
                    xowl.generator.ClassGenerator c = instance.getType();
                    if (c.getJavaName().equals("java.lang.Class")) {
                        String className = c.getPackage().getModel().getBasePackage() + ".";
                        className +=  c.getPackage().getModel().getPackage(instance.getOWLInstance().getInterpretationOf().getContainedBy()).getName() + ".";
                        className +=  instance.getName();
                        writer.write("        public " + type + " get() { return " + className + ".class; }\n");
                    } else
                        writer.write("        public " + type + " get() { return " + c.getJavaName() + ".get_" + instance.getName() + "(); }\n");
                } else {
                    xowl.lang.runtime.Literal value = impl.hasDataValues().get(0);
                    xowl.generator.JavaDatatype datatype = impl.getProperty().getJavaDatatype(value.getMemberOf());
                    String data = datatype.getToValue("\"" + value.getLexicalValue() + "\"");
                    writer.write("        public " + type + " get() { return " + data + "; }\n");
                }
            }
        } else {
            writer.append("        private java.util.List<" + type + "> data;\n");
            writer.append("        public java.util.Collection<" + type + "> get_raw() { return new java.util.ArrayList<" + type + ">(data); }\n");
            if (!impl.hasValues())
                writer.append("        public java.util.Collection<" + type + "> get() { return new java.util.ArrayList<" + type + ">(data); }\n");
            else {
                writer.append("        public java.util.Collection<" + type + "> get() {\n");
                writer.append("            java.util.List<" + type + "> temp = new java.util.ArrayList<" + type + ">(data);\n");
                if (impl.hasSelf())
                    writer.append("            temp.add(domain);\n");
                else if (impl.getProperty().isObjectProperty()) {
                    for (xowl.generator.InstanceGenerator instance : impl.hasObjectValues()) {
                        xowl.generator.ClassGenerator c = instance.getType();
                        if (c.getJavaName().equals("java.lang.Class")) {
                            String className = c.getPackage().getModel().getBasePackage() + ".";
                            className +=  c.getPackage().getModel().getPackage(instance.getOWLInstance().getInterpretationOf().getContainedBy()).getName() + ".";
                            className +=  instance.getName();
                            writer.write("            temp.add(" + className + ".class);\n");
                        } else
                            writer.write("            temp.add(" + c.getJavaName() + ".get_" + instance.getName() + "());\n");
                    }
                } else {
                    for (xowl.lang.runtime.Literal value : impl.hasDataValues()) {
                        xowl.generator.JavaDatatype datatype = impl.getProperty().getJavaDatatype(value.getMemberOf());
                        String data = datatype.getToValue("\"" + value.getLexicalValue() + "\"");
                        writer.write("            temp.add(" + data + ");\n");
                    }
                }
                writer.append("            return temp;\n");
                writer.append("        }\n");
            }
        }
    }
    private static void write_PropertyClass_Implementation_CheckCard(java.io.Writer writer, xowl.generator.PropertyImplementation impl) throws java.io.IOException {
        if (!impl.isVector()) {
            writer.append("        private boolean check_card(int modifier) {\n");
            writer.append("            int card = modifier + " + impl.getValuesCount() + ";\n");
            writer.append("            if (data != null) card++;\n");
            writer.append("            return (card >= " + Integer.toString(impl.getCardMin()) + " && card <= " + Integer.toString(impl.getCardMax()) + ");\n");
            writer.append("        }\n");
        } else {
            writer.append("        private boolean check_card(int modifier) {\n");
            writer.append("            int card = data.size() + " + impl.getValuesCount() + " + modifier;\n");
            writer.append("            return (card >= " + Integer.toString(impl.getCardMin()) + " && card <= " + Integer.toString(impl.getCardMax()) + ");\n");
            writer.append("        }\n");
        }
    }
    private static void write_PropertyClass_Implementation_SimpleCheckAdd(java.io.Writer writer, xowl.generator.PropertyImplementation impl) throws java.io.IOException {
        writer.append("            if (check_contains(elem)) return false;\n");
        writer.append("            if (!check_card(1)) return false;\n");
        if (impl.getProperty().isObjectProperty() && impl.getProperty().isAsymmetric() && impl.getRangeClass().isCompatibleWith(impl.getDomain()) && impl.getParentClass().isCompatibleWith(impl.getRange()))
            writer.append("            if (elem.__getImplOf" + impl.getProperty().getName() + "().check_contains(domain)) return false;\n");
        if (impl.getProperty().isObjectProperty() && impl.getProperty().isIrreflexive() && impl.getParentClass().isCompatibleWith(impl.getRangeClass()))
            writer.append("            if (elem == domain) return false;\n");
        writer.append("            return true;\n");
    }
    private static void write_PropertyClass_Implementation_SimpleCheckRemove(java.io.Writer writer, xowl.generator.PropertyImplementation impl) throws java.io.IOException {
        writer.append("            if (!check_contains(elem)) return false;\n");
        writer.append("            if (!check_card(-1)) return false;\n");
        if (impl.getProperty().isObjectProperty() && impl.getProperty().isReflexive() || impl.hasSelf())
            writer.append("            if (elem == domain) return false;\n");
        writer.append("            return true;\n");
    }
    private static void write_PropertyClass_Implementation_SimpleCheckReplace(java.io.Writer writer, xowl.generator.PropertyImplementation impl) throws java.io.IOException {
        writer.append("            if (check_contains(newElem)) return false;\n");
        writer.append("            if (!check_contains(oldElem)) return false;\n");
        if (impl.getProperty().isObjectProperty() && impl.getProperty().isAsymmetric() && impl.getRangeClass().isCompatibleWith(impl.getDomain()) && impl.getParentClass().isCompatibleWith(impl.getRange()))
            writer.append("            if (newElem.__getImplOf" + impl.getProperty().getName() + "().check_contains(domain)) return false;\n");
        if (impl.getProperty().isObjectProperty() && impl.getProperty().isReflexive() || impl.hasSelf())
            writer.append("            if (oldElem == domain) return false;\n");
        if (impl.getProperty().isObjectProperty() && impl.getProperty().isIrreflexive() && impl.getParentClass().isCompatibleWith(impl.getRangeClass()))
            writer.append("            if (newElem == domain) return false;\n");
        writer.append("            return true;\n");
    }
    private static void write_PropertyClass_Implementation_SimpleDoAdd(java.io.Writer writer, xowl.generator.PropertyImplementation impl) throws java.io.IOException {
        if (!impl.isVector())
            writer.append("            data = elem;\n");
        else
            writer.append("            data.add(elem);\n");
    }
    private static void write_PropertyClass_Implementation_SimpleDoRemove(java.io.Writer writer, xowl.generator.PropertyImplementation impl) throws java.io.IOException {
        if (!impl.isVector())
            writer.append("            data = null;\n");
        else
            writer.append("            data.remove(elem);\n");
    }
    private static void write_PropertyClass_Implementation_Simple(java.io.Writer writer, String type, xowl.generator.PropertyImplementation impl) throws java.io.IOException {
        writer.append("        public boolean simple_check_add(" + type + " elem) {\n");
        write_PropertyClass_Implementation_SimpleCheckAdd(writer, impl);
        writer.append("        }\n");
        writer.append("        public boolean simple_check_remove(" + type + " elem) {\n");
        write_PropertyClass_Implementation_SimpleCheckRemove(writer, impl);
        writer.append("        }\n");
        writer.append("        public boolean simple_check_replace(" + type + " oldElem, " + type + "  newElem) {\n");
        write_PropertyClass_Implementation_SimpleCheckReplace(writer, impl);
        writer.append("        }\n");
        writer.append("        public void simple_add(" + type + " elem) {\n");
        write_PropertyClass_Implementation_SimpleDoAdd(writer, impl);
        writer.append("        }\n");
        writer.append("        public void simple_remove(" + type + " elem) {\n");
        write_PropertyClass_Implementation_SimpleDoRemove(writer, impl);
        writer.append("        }\n");
    }
    private static void write_PropertyClass_Implementation_TreeCheckAdd(java.io.Writer writer, xowl.generator.PropertyImplementation impl) throws java.io.IOException {
        writer.append("            if (!simple_check_add(elem)) return false;\n");
        for (xowl.generator.PropertyImplementation ancestor : impl.getAncestors())
            writer.append("            if (!domain." + ancestor.getProperty().getName() + "_data.simple_check_add(elem)) return false;\n");
        writer.append("            return true;\n");
    }
    private static void write_PropertyClass_Implementation_TreeCheckRemove(java.io.Writer writer, xowl.generator.PropertyImplementation impl) throws java.io.IOException {
        writer.append("            if (!simple_check_remove(elem)) return false;\n");
        for (xowl.generator.PropertyImplementation ancestor : impl.getAncestors())
            writer.append("            if (!domain." + ancestor.getProperty().getName() + "_data.simple_check_remove(elem)) return false;\n");
        writer.append("            return true;\n");
    }
    private static void write_PropertyClass_Implementation_TreeCheckReplace(java.io.Writer writer, xowl.generator.PropertyImplementation impl) throws java.io.IOException {
        writer.append("            if (!simple_check_replace(oldElem, newElem)) return false;\n");
        for (xowl.generator.PropertyImplementation ancestor : impl.getAncestors())
            writer.append("            if (!domain." + ancestor.getProperty().getName() + "_data.simple_check_replace(oldElem, newElem)) return false;\n");
        writer.append("            return true;\n");
    }
    private static void write_PropertyClass_Implementation_TreeDoAdd(java.io.Writer writer, xowl.generator.PropertyImplementation impl) throws java.io.IOException {
        writer.append("            simple_add(elem);\n");
        for (xowl.generator.PropertyImplementation ancestor : impl.getAncestors())
            writer.append("            domain." + ancestor.getProperty().getName() + "_data.simple_add(elem);\n");
    }
    private static void write_PropertyClass_Implementation_TreeDoRemove(java.io.Writer writer, xowl.generator.PropertyImplementation impl) throws java.io.IOException {
        writer.append("            simple_remove(elem);\n");
        for (xowl.generator.PropertyImplementation ancestor : impl.getAncestors())
            writer.append("            domain." + ancestor.getProperty().getName() + "_data.simple_remove(elem);\n");
    }
    private static void write_PropertyClass_Implementation_Tree(java.io.Writer writer, String type, xowl.generator.PropertyImplementation impl) throws java.io.IOException {
        writer.append("        private boolean tree_check_add(" + type + " elem) {\n");
        write_PropertyClass_Implementation_TreeCheckAdd(writer, impl);
        writer.append("        }\n");
        writer.append("        private boolean tree_check_remove(" + type + " elem) {\n");
        write_PropertyClass_Implementation_TreeCheckRemove(writer, impl);
        writer.append("        }\n");
        writer.append("        private boolean tree_check_replace(" + type + " oldElem, " + type + "  newElem) {\n");
        write_PropertyClass_Implementation_TreeCheckReplace(writer, impl);
        writer.append("        }\n");
        writer.append("        private void tree_add(" + type + " elem) {\n");
        write_PropertyClass_Implementation_TreeDoAdd(writer, impl);
        writer.append("        }\n");
        writer.append("        private void tree_remove(" + type + " elem) {\n");
        write_PropertyClass_Implementation_TreeDoRemove(writer, impl);
        writer.append("        }\n");
    }
    private static void write_PropertyClass_Implementation_UserCheckAdd(java.io.Writer writer, xowl.generator.PropertyImplementation impl) throws java.io.IOException {
        // Try delegating to a descendant
        for (xowl.generator.PropertyImplementation descandant : impl.getDescendants()) {
            writer.append("            if (elem instanceof " + descandant.getRangeClass().getJavaName() + ")\n");
            writer.append("                return domain." + descandant.getProperty().getName() + "_data.user_check_add((" + descandant.getRangeClass().getJavaName() + ")elem);\n");
        }
        // Dispatch to inverse
        if (!impl.getInverses().isEmpty())
            writer.append("            if (!elem.__getImplOf" + impl.getInverses().get(0).getName() + "().inverse_check_add(domain)) return false;\n");
        writer.append("            return tree_check_add(elem);\n");
    }
    private static void write_PropertyClass_Implementation_UserCheckRemove(java.io.Writer writer, xowl.generator.PropertyImplementation impl) throws java.io.IOException {
        // Try delegating to a descendant
        for (xowl.generator.PropertyImplementation descandant : impl.getDescendants()) {
            writer.append("            if (elem instanceof " + descandant.getRangeClass().getJavaName() + ")\n");
            writer.append("                return domain." + descandant.getProperty().getName() + "_data.user_check_remove((" + descandant.getRangeClass().getJavaName() + ")elem);\n");
        }
        // Dispatch to inverse
        if (!impl.getInverses().isEmpty())
            writer.append("            if (!elem.__getImplOf" + impl.getInverses().get(0).getName() + "().inverse_check_remove(domain)) return false;\n");
        writer.append("            return tree_check_remove(elem);\n");
    }
    private static void write_PropertyClass_Implementation_UserCheckReplace(java.io.Writer writer, xowl.generator.PropertyImplementation impl) throws java.io.IOException {
        // Try delegating to a descendant
        for (xowl.generator.PropertyImplementation descandant : impl.getDescendants()) {
            writer.append("            if (newElem instanceof " + descandant.getRangeClass().getJavaName() + ")\n");
            writer.append("                return domain." + descandant.getProperty().getName() + "_data.user_check_replace((" + descandant.getRangeClass().getJavaName() + ")oldElem, (" + descandant.getRangeClass().getJavaName() + ")newElem);\n");
        }
        // Dispatch to inverse
        if (!impl.getInverses().isEmpty()) {
            writer.append("            if (!oldElem.__getImplOf" + impl.getInverses().get(0).getName() + "().inverse_check_remove(domain)) return false;\n");
            writer.append("            if (!newElem.__getImplOf" + impl.getInverses().get(0).getName() + "().inverse_check_add(domain)) return false;\n");
        }
        writer.append("            return tree_check_replace(oldElem, newElem);\n");
    }
    private static void write_PropertyClass_Implementation_UserDoAdd(java.io.Writer writer, xowl.generator.PropertyImplementation impl) throws java.io.IOException {
        // Try delegating to a descendant
        for (xowl.generator.PropertyImplementation descandant : impl.getDescendants()) {
            writer.append("            if (elem instanceof " + descandant.getRangeClass().getJavaName() + ") {\n");
            writer.append("                domain." + descandant.getProperty().getName() + "_data.user_add((" + descandant.getRangeClass().getJavaName() + ")elem);\n");
            writer.append("                return;\n");
            writer.append("            }\n");
        }
        // Dispatch to inverse
        if (!impl.getInverses().isEmpty())
            writer.append("            elem.__getImplOf" + impl.getInverses().get(0).getName() + "().inverse_add(domain);\n");
        writer.append("            tree_add(elem);\n");
    }
    private static void write_PropertyClass_Implementation_UserDoRemove(java.io.Writer writer, xowl.generator.PropertyImplementation impl) throws java.io.IOException {
        // Try delegating to a descendant
        for (xowl.generator.PropertyImplementation descandant : impl.getDescendants()) {
            writer.append("            if (elem instanceof " + descandant.getRangeClass().getJavaName() + ") {\n");
            writer.append("                domain." + descandant.getProperty().getName() + "_data.user_remove((" + descandant.getRangeClass().getJavaName() + ")elem);\n");
            writer.append("                return;\n");
            writer.append("            }\n");
        }
        // Dispatch to inverse
        if (!impl.getInverses().isEmpty())
            writer.append("            elem.__getImplOf" + impl.getInverses().get(0).getName() + "().inverse_remove(domain);\n");
        writer.append("            tree_remove(elem);\n");
    }
    private static void write_PropertyClass_Implementation_InverseCheckAdd(java.io.Writer writer, xowl.generator.PropertyImplementation impl) throws java.io.IOException {
        // Try delegating to a descendant
        for (xowl.generator.PropertyImplementation descandant : impl.getDescendants()) {
            writer.append("            if (elem instanceof " + descandant.getRangeClass().getJavaName() + ")\n");
            writer.append("                return domain." + descandant.getProperty().getName() + "_data.inverse_check_add((" + descandant.getRangeClass().getJavaName() + ")elem);\n");
        }
        writer.append("            return tree_check_add(elem);\n");
    }
    private static void write_PropertyClass_Implementation_InverseCheckRemove(java.io.Writer writer, xowl.generator.PropertyImplementation impl) throws java.io.IOException {
        // Try delegating to a descendant
        for (xowl.generator.PropertyImplementation descandant : impl.getDescendants()) {
            writer.append("            if (elem instanceof " + descandant.getRangeClass().getJavaName() + ")\n");
            writer.append("                return domain." + descandant.getProperty().getName() + "_data.inverse_check_remove((" + descandant.getRangeClass().getJavaName() + ")elem);\n");
        }
        writer.append("            return tree_check_remove(elem);\n");
    }
    private static void write_PropertyClass_Implementation_InverseCheckReplace(java.io.Writer writer, xowl.generator.PropertyImplementation impl) throws java.io.IOException {
        // Try delegating to a descendant
        for (xowl.generator.PropertyImplementation descandant : impl.getDescendants()) {
            writer.append("            if (newElem instanceof " + descandant.getRangeClass().getJavaName() + ")\n");
            writer.append("                return domain." + descandant.getProperty().getName() + "_data.inverse_check_replace((" + descandant.getRangeClass().getJavaName() + ")oldElem, (" + descandant.getRangeClass().getJavaName() + ")newElem);\n");
        }
        writer.append("            return tree_check_replace(oldElem, newElem);\n");
    }
    private static void write_PropertyClass_Implementation_InverseDoAdd(java.io.Writer writer, xowl.generator.PropertyImplementation impl) throws java.io.IOException {
        // Try delegating to a descendant
        for (xowl.generator.PropertyImplementation descandant : impl.getDescendants()) {
            writer.append("            if (elem instanceof " + descandant.getRangeClass().getJavaName() + ") {\n");
            writer.append("                domain." + descandant.getProperty().getName() + "_data.inverse_add((" + descandant.getRangeClass().getJavaName() + ")elem);\n");
            writer.append("                return;\n");
            writer.append("            }\n");
        }
        writer.append("            tree_add(elem);\n");
    }
    private static void write_PropertyClass_Implementation_InverseDoRemove(java.io.Writer writer, xowl.generator.PropertyImplementation impl) throws java.io.IOException {
        // Try delegating to a descendant
        for (xowl.generator.PropertyImplementation descandant : impl.getDescendants()) {
            writer.append("            if (elem instanceof " + descandant.getRangeClass().getJavaName() + ") {\n");
            writer.append("                domain." + descandant.getProperty().getName() + "_data.inverse_remove((" + descandant.getRangeClass().getJavaName() + ")elem);\n");
            writer.append("                return;\n");
            writer.append("            }\n");
        }
        writer.append("            tree_remove(elem);\n");
    }
    private static void write_PropertyClass_Implementation_Constructor(java.io.Writer writer, String type, xowl.generator.PropertyImplementation impl) throws java.io.IOException {
        writer.append("        public " + impl.getProperty().getName() + "_impl(" + impl.getParentClass().getJavaName() + " domain) {\n");
        writer.append("            this.domain = domain;\n");
        if (impl.isVector())
            writer.append("            this.data = new java.util.ArrayList<" + type + ">();\n");
        writer.append("        }\n");
    }
    private static void write_PropertyClass_Implementation_SameType(java.io.Writer writer, String type, xowl.generator.PropertyImplementation impl) throws java.io.IOException {
        write_PropertyClass_Implementation_Header(writer, impl);
        write_PropertyClass_Implementation_InnerData(writer, type, impl);

        write_PropertyClass_Implementation_CheckCard(writer, impl);
        if (!impl.isVector()) {
            if (impl.getProperty().isObjectProperty())
                writer.append("        @Override public boolean check_contains(" + type + " elem) { return (data == elem); }\n");
            else
                writer.append("        @Override public boolean check_contains(" + type + " elem) { return data.equals(elem); }\n");
        } else
            writer.append("        @Override public boolean check_contains(" + type + " elem) { return (data.contains(elem)); }\n");

        write_PropertyClass_Implementation_Simple(writer, type, impl);
        write_PropertyClass_Implementation_Tree(writer, type, impl);
        
        writer.append("        @Override public boolean user_check_add(" + type + " elem) {\n");
        write_PropertyClass_Implementation_UserCheckAdd(writer, impl);
        writer.append("        }\n");
        writer.append("        @Override public boolean user_check_remove(" + type + " elem) {\n");
        write_PropertyClass_Implementation_UserCheckRemove(writer, impl);
        writer.append("        }\n");
        writer.append("        @Override public boolean user_check_replace(" + type + " oldElem, " + type + "  newElem) {\n");
        write_PropertyClass_Implementation_UserCheckReplace(writer, impl);
        writer.append("        }\n");
        writer.append("        @Override public void user_add(" + type + " elem) {\n");
        write_PropertyClass_Implementation_UserDoAdd(writer, impl);
        writer.append("        }\n");
        writer.append("        @Override public void user_remove(" + type + " elem) {\n");
        write_PropertyClass_Implementation_UserDoRemove(writer, impl);
        writer.append("        }\n");

        writer.append("        @Override public boolean inverse_check_add(" + type + " elem) {\n");
        write_PropertyClass_Implementation_InverseCheckAdd(writer, impl);
        writer.append("        }\n");
        writer.append("        @Override public boolean inverse_check_remove(" + type + " elem) {\n");
        write_PropertyClass_Implementation_InverseCheckRemove(writer, impl);
        writer.append("        }\n");
        writer.append("        @Override public boolean inverse_check_replace(" + type + " oldElem, " + type + "  newElem) {\n");
        write_PropertyClass_Implementation_InverseCheckReplace(writer, impl);
        writer.append("        }\n");
        writer.append("        @Override public void inverse_add(" + type + " elem) {\n");
        write_PropertyClass_Implementation_InverseDoAdd(writer, impl);
        writer.append("        }\n");
        writer.append("        @Override public void inverse_remove(" + type + " elem) {\n");
        write_PropertyClass_Implementation_InverseDoRemove(writer, impl);
        writer.append("        }\n");

        write_PropertyClass_Implementation_Constructor(writer, type, impl);
        writer.append("    }\n");
    }
    private static void write_PropertyClass_Implementation_Transtype(java.io.Writer writer, String baseType, String implType, xowl.generator.PropertyImplementation impl) throws java.io.IOException {
        write_PropertyClass_Implementation_Header(writer, impl);
        write_PropertyClass_Implementation_InnerData(writer, implType, impl);

        write_PropertyClass_Implementation_CheckCard(writer, impl);
        if (!impl.isVector()) {
            if (impl.getProperty().isObjectProperty())
                writer.append("        public boolean check_contains(" + implType + " elem) { return (data == elem); }\n");
            else
                writer.append("        public boolean check_contains(" + implType + " elem) { return data.equals(elem); }\n");
            writer.append("        @Override public boolean check_contains(" + baseType + " elem) {\n");
            writer.append("            if (!(elem instanceof " + implType + ")) return false;\n");
            writer.append("            return (data == elem);\n");
            writer.append("        }\n");
        } else {
            writer.append("        public boolean check_contains(" + implType + " elem) { return (data.contains(elem)); }\n");
            writer.append("        @Override public boolean check_contains(" + baseType + " elem) {\n");
            writer.append("            if (!(elem instanceof " + implType + ")) return false;\n");
            writer.append("            return (data.contains((" + implType + ")elem));\n");
            writer.append("        }\n");
        }

        write_PropertyClass_Implementation_Simple(writer, implType, impl);
        write_PropertyClass_Implementation_Tree(writer, implType, impl);

        writer.append("        public boolean user_check_add(" + implType + " elem) {\n");
        write_PropertyClass_Implementation_UserCheckAdd(writer, impl);
        writer.append("        }\n");
        writer.append("        public boolean user_check_remove(" + implType + " elem) {\n");
        write_PropertyClass_Implementation_UserCheckRemove(writer, impl);
        writer.append("        }\n");
        writer.append("        public boolean user_check_replace(" + implType + " oldElem, " + implType + "  newElem) {\n");
        write_PropertyClass_Implementation_UserCheckReplace(writer, impl);
        writer.append("        }\n");
        writer.append("        public void user_add(" + implType + " elem) {\n");
        write_PropertyClass_Implementation_UserDoAdd(writer, impl);
        writer.append("        }\n");
        writer.append("        public void user_remove(" + implType + " elem) {\n");
        write_PropertyClass_Implementation_UserDoRemove(writer, impl);
        writer.append("        }\n");
        writer.append("        @Override public boolean user_check_add(" + baseType + " elem) { return user_check_add((" + implType + ")elem); }\n");
        writer.append("        @Override public boolean user_check_remove(" + baseType + " elem) { return user_check_remove((" + implType + ")elem); }\n");
        writer.append("        @Override public boolean user_check_replace(" + baseType + " oldElem, " + baseType + "  newElem) { return user_check_replace((" + implType + ")oldElem, (" + implType + ")newElem); }\n");
        writer.append("        @Override public void user_add(" + baseType + " elem) { user_add((" + implType + ")elem); }\n");
        writer.append("        @Override public void user_remove(" + baseType + " elem) { user_remove((" + implType + ")elem); }\n");

        writer.append("        public boolean inverse_check_add(" + implType + " elem) {\n");
        write_PropertyClass_Implementation_InverseCheckAdd(writer, impl);
        writer.append("        }\n");
        writer.append("        public boolean inverse_check_remove(" + implType + " elem) {\n");
        write_PropertyClass_Implementation_InverseCheckRemove(writer, impl);
        writer.append("        }\n");
        writer.append("        public boolean inverse_check_replace(" + implType + " oldElem, " + implType + "  newElem) {\n");
        write_PropertyClass_Implementation_InverseCheckReplace(writer, impl);
        writer.append("        }\n");
        writer.append("        public void inverse_add(" + implType + " elem) {\n");
        write_PropertyClass_Implementation_InverseDoAdd(writer, impl);
        writer.append("        }\n");
        writer.append("        public void inverse_remove(" + implType + " elem) {\n");
        write_PropertyClass_Implementation_InverseDoRemove(writer, impl);
        writer.append("        }\n");
        writer.append("        @Override public boolean inverse_check_add(" + baseType + " elem) { return inverse_check_add((" + implType + ")elem); }\n");
        writer.append("        @Override public boolean inverse_check_remove(" + baseType + " elem) { return inverse_check_remove((" + implType + ")elem); }\n");
        writer.append("        @Override public boolean inverse_check_replace(" + baseType + " oldElem, " + baseType + "  newElem) { return inverse_check_replace((" + implType + ")oldElem, (" + implType + ")newElem); }\n");
        writer.append("        @Override public void inverse_add(" + baseType + " elem) { inverse_add((" + implType + ")elem); }\n");
        writer.append("        @Override public void inverse_remove(" + baseType + " elem) { inverse_remove((" + implType + ")elem); }\n");

        write_PropertyClass_Implementation_Constructor(writer, implType, impl);
        writer.append("    }\n");
    }

    private static void write_Datatype(java.io.Writer writer, xowl.generator.PropertyImplementation impl) throws java.io.IOException {
        for (xowl.generator.PropertyInterface inter : impl.getInterfaces()) {
            if (inter.isVector())
                write_DatatypeInterfaceVector(writer, inter, impl);
            else
                write_DatatypeInterfaceScalar(writer, inter, impl);
        }
    }
    private static void write_DatatypeInterfaceScalar(java.io.Writer writer, xowl.generator.PropertyInterface inter, xowl.generator.PropertyImplementation impl) throws java.io.IOException {
        String type = impl.getRepresentationRange();
        String property = inter.getProperty().getName();
        property = String.valueOf(property.charAt(0)).toUpperCase() + property.substring(1);
        writer.append("    public boolean set" + property + "(" + type + " elem) {\n");
        writer.append("        data" + property + ".simple_add(elem);\n");
        writer.append("        return true;\n");
        writer.append("    }\n");
        writer.append("    public " + type + " get" + property + "() { return data" + property + ".get(); }\n");
    }
    private static void write_DatatypeInterfaceVector(java.io.Writer writer, xowl.generator.PropertyInterface inter, xowl.generator.PropertyImplementation impl) throws java.io.IOException {
        String type = impl.getRepresentationRange();
        String property = inter.getProperty().getName();
        property = String.valueOf(property.charAt(0)).toUpperCase() + property.substring(1);
        writer.append("    public boolean add" + property + "(" + type + " elem) {\n");
        writer.append("        if (!data" + property + ".user_check_add(elem)) return false;\n");
        writer.append("        data" + property + ".user_add(elem);\n");
        writer.append("        return true;\n");
        writer.append("    }\n");
        writer.append("    public boolean remove" + property + "(" + type + " elem) {\n");
        writer.append("        if (!data" + property + ".user_check_remove(elem)) return false;\n");
        writer.append("        data" + property + ".user_remove(elem);\n");
        writer.append("        return true;\n");
        writer.append("    }\n");
        writer.append("    public java.util.Collection<" + type + "> getAll" + property + "() { return data" + property + ".get(); }\n");
    }

    private static void write_ObjectInterface(java.io.Writer writer, xowl.generator.PropertyInterface inter, xowl.generator.PropertyImplementation impl, boolean isInTypeRestrictChain) throws java.io.IOException {
        xowl.generator.ClassGenerator interfaceRange = inter.getRangeClass();
        if (inter.isVector()) {
            if (interfaceRange == impl.getRangeClass())
                write_ObjectInterfaceVector_SameType(writer, inter, impl, isInTypeRestrictChain);
            else
                write_ObjectInterfaceVector_Transtype(writer, inter, impl);
        } else {
            if (interfaceRange == impl.getRangeClass())
                write_ObjectInterfaceScalar_SameType(writer, inter, impl, isInTypeRestrictChain);
            else
                write_ObjectInterfaceScalar_Transtype(writer, inter, impl);
        }
    }
    private static void write_ObjectInterfaceScalar_SameType(java.io.Writer writer, xowl.generator.PropertyInterface inter, xowl.generator.PropertyImplementation impl, boolean isInTypeRestrictChain) throws java.io.IOException {
        String interType = impl.getRangeClass().getJavaName();
        String property = inter.getProperty().getName();
        property = String.valueOf(property.charAt(0)).toUpperCase() + property.substring(1);
        writer.append("    public boolean set" + property + "(" + interType + " elem) {\n");
        writer.append("        if (data" + property + ".get() != null) {\n");
        writer.append("            if (elem == null) {\n");
        writer.append("                if (!data" + property + ".user_check_remove(data" + property + ".get())) return false;\n");
        writer.append("                data" + property + ".user_remove(data" + property + ".get());\n");
        writer.append("            } else {\n");
        writer.append("                if (!data" + property + ".user_check_replace(data" + property + ".get(), elem)) return false;\n");
        writer.append("                data" + property + ".user_remove(data" + property + ".get());\n");
        writer.append("                data" + property + ".user_add(elem);\n");
        writer.append("            }\n");
        writer.append("        } else {\n");
        writer.append("            if (elem == null) return true;\n");
        writer.append("            if (!data" + property + ".user_check_add(elem)) return false;\n");
        writer.append("            data" + property + ".user_add(elem);\n");
        writer.append("        }\n");
        writer.append("        return true;\n");
        writer.append("    }\n");
        if (!isInTypeRestrictChain)
            writer.append("    public " + interType + " get" + property + "() { return data" + property + ".get(); }\n");
        else
            writer.append("    public " + interType + " get" + property + "As(" + interType + " type) { return data" + property + ".get(); }\n");
    }
    private static void write_ObjectInterfaceScalar_Transtype(java.io.Writer writer, xowl.generator.PropertyInterface inter, xowl.generator.PropertyImplementation impl) throws java.io.IOException {
        String implType = impl.getRangeClass().getJavaName();
        String interType = inter.getRepresentationRange();
        String property = inter.getProperty().getName();
        property = String.valueOf(property.charAt(0)).toUpperCase() + property.substring(1);
        writer.append("    public boolean set" + property + "(" + interType + " elem) {\n");
        writer.append("        if (data" + property + ".get() != null) {\n");
        writer.append("            if (elem == null) {\n");
        writer.append("                if (!data" + property + ".user_check_remove(data" + property + ".get())) return false;\n");
        writer.append("                data" + property + ".user_remove(data" + property + ".get());\n");
        writer.append("            } else {\n");
        writer.append("                if (!data" + property + ".user_check_replace(data" + property + ".get(), (" + implType + ")elem)) return false;\n");
        writer.append("                data" + property + ".user_remove(data" + property + ".get());\n");
        writer.append("                data" + property + ".user_add((" + implType + ")elem);\n");
        writer.append("            }\n");
        writer.append("        } else {\n");
        writer.append("            if (elem == null) return true;\n");
        writer.append("            if (!data" + property + ".user_check_add((" + implType + ")elem)) return false;\n");
        writer.append("            data" + property + ".user_add((" + implType + ")elem);\n");
        writer.append("        }\n");
        writer.append("        return true;\n");
        writer.append("    }\n");
        writer.append("    public " + interType + " get" + property + "As(" + interType + " type) { return data" + property + ".get(); }\n");
    }
    private static void write_ObjectInterfaceVector_SameType(java.io.Writer writer, xowl.generator.PropertyInterface inter, xowl.generator.PropertyImplementation impl, boolean isInTypeRestrictChain) throws java.io.IOException {
        String interType = impl.getRangeClass().getJavaName();
        String property = inter.getProperty().getName();
        property = String.valueOf(property.charAt(0)).toUpperCase() + property.substring(1);
        writer.append("    public boolean add" + property + "(" + interType + " elem) {\n");
        writer.append("        if (!data" + property + ".user_check_add(elem)) return false;\n");
        writer.append("        data" + property + ".user_add(elem);\n");
        writer.append("        return true;\n");
        writer.append("    }\n");

        writer.append("    public boolean remove" + property + "(" + interType + " elem) {\n");
        writer.append("        if (!data" + property + ".user_check_remove(elem)) return false;\n");
        writer.append("        data" + property + ".user_remove(elem);\n");
        writer.append("        return true;\n");
        writer.append("    }\n");
        if (impl.isVector()) {
            if (!isInTypeRestrictChain)
                writer.append("    public java.util.Collection<" + interType + "> getAll" + property + "() { return data" + property + ".get(); }\n");
            else
                writer.append("    public java.util.Collection<" + interType + "> getAll" + property + "As(" + interType + " type) { return data" + property + ".get(); }\n");
        } else {
            if (!isInTypeRestrictChain)
                writer.append("    public java.util.Collection<" + interType + "> getAll" + property + "() {\n");
            else
                writer.append("    public java.util.Collection<" + interType + "> getAll" + property + "As(" + interType + " type) {\n");
            writer.append("        java.util.List<" + interType + "> result = new java.util.ArrayList<" + interType + ">();\n");
            writer.append("        if (data" + property + ".get() != null)\n");
            writer.append("            result.add(data" + property + ".get());\n");
            writer.append("        return result;\n");
            writer.append("    }\n");
        }
    }
    private static void write_ObjectInterfaceVector_Transtype(java.io.Writer writer, xowl.generator.PropertyInterface inter, xowl.generator.PropertyImplementation impl) throws java.io.IOException {
        String implType = impl.getRangeClass().getJavaName();
        String interType = inter.getRepresentationRange();
        String property = inter.getProperty().getName();
        property = String.valueOf(property.charAt(0)).toUpperCase() + property.substring(1);
        writer.append("    public boolean add" + property + "(" + interType + " elem) {\n");
        writer.append("        " + implType + " value = (" + implType + ")elem;\n");
        writer.append("        if (!data" + property + ".user_check_add(value)) return false;\n");
        writer.append("        data" + property + ".user_add(value);\n");
        writer.append("        return true;\n");
        writer.append("    }\n");

        writer.append("    public boolean remove" + property + "(" + interType + " elem) {\n");
        writer.append("        " + implType + " value = (" + implType + ")elem;\n");
        writer.append("        if (!data" + property + ".user_check_remove(value)) return false;\n");
        writer.append("        data" + property + ".user_remove(value);\n");
        writer.append("        return true;\n");
        writer.append("    }\n");
        writer.append("    public java.util.Collection<" + interType + "> getAll" + property + "As(" + interType + " type) {\n");
        writer.append("        java.util.List<" + interType + "> result = new java.util.ArrayList<" + interType + ">();\n");
        if (impl.isVector()) {
            writer.append("        for (" + implType + " value : data" + property + ".get())\n");
            writer.append("            result.add(value);\n");
        } else {
            writer.append("        if (data" + property + ".get() != null)\n");
            writer.append("            result.add(data" + property + ".get());\n");
        }
        writer.append("        return result;\n");
        writer.append("    }\n");
    }

    public static void write_constructor(java.io.Writer writer, xowl.generator.PropertyImplementation impl) throws java.io.IOException {
        String property = impl.getProperty().getName();
        property = String.valueOf(property.charAt(0)).toUpperCase() + property.substring(1);
        writer.write("        data" + property + " = new " + impl.getProperty().getName() + "_impl(this);\n");
    }
}
